/*
 *  Copyright 2008 Mark Ashworth <javameme@gmail.com>.
 * 
 *  Licensed under the GNU General Public License v3 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.gnu.org/licenses/gpl-3.0.html
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package xperiment.metaphor.model.entity;

import xperiment.metaphor.model.entity.Department;
import xperiment.metaphor.model.*;
import metaphor.service.ServiceException;

import java.util.Date;
import java.util.Calendar;

import org.apache.commons.lang.time.DateUtils;

/**
 * Job position
 *
 * @author Mark Ashworth
 * @version 1.0.0
 */
public class Position extends DomainObject implements Persistent {
    
    /* The unique id of the position */
    //@Id @GeneratedValue(strategy=GenerationType.AUTO)
    //@Column(name = "position_id", unique = true, nullable = false)
    private Long id = null;

    /** The date the position was approved */
    private Date approved;

    /** The date the position was advertised */
    private Date advertised;

    /** The date the position was appointed */
    private Date appointed;

    /** The date the position was discharged */
    private Date discharged;

    /** Whether the position is an affirmative action position */
    private boolean affirmativeAction;

	/** The job description of the position */
	private JobDescription jobDescription;
    
	/** The department that the position is in */
	private Department department;

    /** The employee that is in the position */
    private Long employeeId;

    /* Whether the position is enabled */
    private boolean enabled = true;
    
    /**
     * Default constructor
     */
    public Position() {
        this(null, null);
    }

    /**
     * Constructs the position with the description and department information
     * @param jobDescription The job description of the position
     * @param department The department of the position
     */
    public Position(JobDescription jobDescription, Department department) {
        super();
        this.jobDescription = jobDescription;
        this.department = department;
    }
    
    /**
     * The unique id of the position
     * @return Long
     */
	@Override
	public Long getId() {
		return this.id;
	}

    public Date getApproved() {
        return approved;
    }

    protected void setApproved(Date approved) {
        Date old = getApproved();
        this.approved = approved;
        firePropertyChange("approved", old, getApproved());
    }

    public Date getAdvertised() {
        return advertised;
    }

    protected void setAdvertised(Date advertised) {
        Date old = getAdvertised();
        this.advertised = advertised;
        firePropertyChange("advertised", old, getAdvertised());
    }

    public Date getAppointed() {
        return appointed;
    }

    protected void setAppointed(Date appointed) {
        Date old = getAppointed();
        this.appointed = appointed;
        firePropertyChange("appointed", old, getAppointed());
    }

    public Date getDischarged() {
        return discharged;
    }

    protected void setDischarged(Date discharged) {
        Date old = getDischarged();
        this.discharged = discharged;
        firePropertyChange("discharged", old, getDischarged());
    }

    public boolean isAffirmativeAction() {
        return affirmativeAction;
    }

    protected void setAffirmativeAction(boolean affirmativeAction) {
        boolean old = isAffirmativeAction();
        this.affirmativeAction = affirmativeAction;
        firePropertyChange("affirmativeAction", old, isAffirmativeAction());
    }

    /**
	 * The department that the position is in. It is possible that the position
	 * is not in a department, for example a CEO might not be assigned to a
	 * department.
	 * @return Department
	 */
	public Department getDepartment() {
		return department;
	}

	/**
	 * The department that the position is assigned to
	 * @param department The new department
	 */
	public void setDepartment(Department department) {
        Department old = getDepartment();
		this.department = department;
        firePropertyChange("department", old, getDepartment());
	}

	/**
	 * The job description of the position
	 * @return JobDescription
	 */
	public JobDescription getJobDescription() {
		return jobDescription;
	}

	/**
	 * The job description of the position
	 * @param job The new job description
	 */
	public void setJobDescription(JobDescription job) {
        JobDescription old = getJobDescription();
		this.jobDescription = job;
        firePropertyChange("jobDescription", old, getJobDescription());
	}

    /**
     * Whether the position is enabled
     * @return boolean
     */
    public boolean isEnabled() {
        return this.enabled;
    }
    
    /**
     * Whether the position is enabled
     * @param enabled The new value
     */
    public void setEnabled(boolean enabled) {
        boolean old = isEnabled();
        this.enabled = enabled;
        firePropertyChange("enabled", old, isEnabled());
    }

    /**
     * Sets the date that the position was approved as now
     * @return Position
     */
    public Position approve() {
        /* Get the date and remove the time */
        Calendar cal = Calendar.getInstance();
        DateUtils.truncate(cal, Calendar.DATE);
        
        this.setApproved(cal.getTime());
        return this;
    }

    /**
     * Sets the date that the position was approved
     * @param date The new value
     * @return Position
     */
    public Position approve(Date date) {
        this.setApproved(date);
        return this;
    }

    /**
     * Sets the date that the position was approved
     * @param year The year (eg. 2008)
     * @param month The month (eg. 05 == May)
     * @param date The day
     * @return Position
     */
    public Position approve(int year, int month, int date) {
        if (month < 1 || month > 12) throw new IllegalArgumentException("The month of " + month + " is invalid.");

        Calendar cal = Calendar.getInstance();
        cal.clear();
        cal.set(year, month + 1, date);

        this.setApproved(cal.getTime());
        return this;
    }

    /**
     * The appoint method which is used by to support the Employee.appoint() method.
     * @param employee The employee that is appointed to the position
     * @param appointmentDate The date of the appointment
     * @return Position
     * @throws ModelException If there was an error with the employee or appointment date
     */
    protected Position appoint(Employee employee, Date appointmentDate) throws ModelException {
        if (appointmentDate == null) throw new ModelException("The appointment date is null");
        if (employee == null) throw new ModelException("The employee cannot be null when appointing");
        if (employee.getId() == null) throw new SaveTransientException("The employee id is null");

        this.setAppointed(appointmentDate);
        this.employeeId = employee.getId();

        return save();
    }

    /**
     * Sets the date that the position was discharged as today
     * @return Position
     */
    public Position discharge() {
        /* Get the date and remove the time */
        Calendar cal = Calendar.getInstance();
        DateUtils.truncate(cal, Calendar.DATE);

        this.setDischarged(cal.getTime());
        this.setEnabled(false);
        return this;
    }

    /**
     * Sets the date that the position was discharged
     * @param date The new value
     * @return Position
     */
    public Position discharge(Date date) {
        this.setDischarged(date);
        this.setEnabled(false);
        return this;
    }

    /**
     * Sets the date that the position was discharged
     * @param year The year (eg. 2008)
     * @param month The month (eg. 05 == May)
     * @param date The day
     * @return Position
     */
    public Position discharge(int year, int month, int date) {
        if (month < 1 || month > 12) throw new IllegalArgumentException("The month of " + month + " is invalid.");

        Calendar cal = Calendar.getInstance();
        cal.clear();
        cal.set(year, month + 1, date);

        this.setDischarged(cal.getTime());
        this.setEnabled(false);
        return this;
    }

    /**
     * Saves the position to the persistent store
     * @throws xperiment.metaphor.model.ModelException If there was an error
     */
    public Position save() throws ModelException {

        try {
            Position d = (Position) getDao().persist(this);
            this.id = d.getId();
            return this;
        } catch (ServiceException e) {
            throw new SaveException("Unable to save position", e);
        }
    }

    /**
     * Returns the String of the job description and department that this
     * position defines.
     * @return String
     */
    @Override
    public String toString() {
        return "[" + getJobDescription() + "] in [" + getDepartment() + "]";
    }
    
    /**
     * &lt;position class="xperiment.metaphor.model.people.employee.Position" id="1" &gt;
     * \t&lt;job_description class="xperiment.metaphor.model.people.employee.JobDescription" id="22" name="Developer" enabled="true" /&gt;
     * \t\t&lt;purpose&gt;&lt;![CDATA[To programme stuff]]&gt;&lt;/purpose&gt;
     * \t&lt;/job_description&gt; 
     * \t&lt;department class="xperiment.metaphor.model.organization.Department" id="1" name="IT" enabled="true" /&gt;
     * &lt;/position&gt;
     * @return
     */
    public String toXML() {
        
        return "<position class='" + getClass().getName() 
                + "' id='" + getId() 
                + "' >"
                + (getJobDescription() == null ? "" : getJobDescription().toXML())
                + (getDepartment() == null ? "" : getDepartment().toXML())
                + "</position>";
    }
}